//The MIT License
//
//Copyright (c) 2009 nodchip
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
package tv.dyndns.kishibe.client;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import tv.dyndns.kishibe.client.packet.PacketUserData;
import tv.dyndns.kishibe.client.setting.PanelSettingChat;
import tv.dyndns.kishibe.client.util.SerialTimer;

import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class UserData implements CloseHandler<Window> {
	private static final UserData INSTANCE = new UserData();

	public static UserData getInstance() {
		return INSTANCE;
	}

	private UserData() {
		data.initialize();
		Window.addCloseHandler(this);
		load();
	}

	private PacketUserData data = new PacketUserData();
	private final List<UserDataLoadListener> loadListeners = new ArrayList<UserDataLoadListener>();
	private boolean ready = false;
	private static final String KEY_PLAYER_NAME = "player_name";
	private static final String KEY_GREETING = "greeting";
	private static final String KEY_HIGHSCORE = "high_score";
	private static final String KEY_AVERAGE = "average_score";
	private static final String KEY_PLAY_COUNT = "play_count";
	private static final String KEY_VICTORY_POINT = "victory_point";
	private static final String KEY_LEVEL_NAME = "level";
	private static final String KEY_LEVEL_NUMBER = "level_number";
	private static final String KEY_AVERAGE_RANK = "avarage_rank";
	private static final String KEY_GENRE = "genre";
	private static final String KEY_TYPE = "type";
	private static final String KEY_CLASS_LEVEL = "class_level";
	private static final String KEY_USER_CODE = "user_code";
	private static final String KEY_REPORT_PROBLEMS_PREFIX = "report_problems";
	private static final String KEY_IMAGE_FILE_NAME = "image_file_name";
	private static final String DELIMITER = " ";

	public void load() {
		// Cookies.setCookie(KEY_USER_CODE, "12345678", getExpireTime());
		final String s = Cookies.getCookie(KEY_USER_CODE);
		if (s == null || s.length() == 0 || s.length() > 8) {
			createUserCode();
			return;
		}
		data.userCode = Integer.parseInt(s);

		if (loadFromCookie()) {
			return;
		}

		loadFromServer();
	}

	private void createUserCode() {
		Service.Util.getInstance().getNewUserCode(callbackCreateUserCode);
	}

	private final AsyncCallback<Integer> callbackCreateUserCode = new AsyncCallback<Integer>() {
		public void onSuccess(Integer result) {
			data.userCode = result;
			Cookies.setCookie(KEY_USER_CODE, "" + data.userCode, getExpireTime());
			save();
			callLoadListeners();
		}

		public void onFailure(Throwable caught) {
		}
	};

	private boolean loadFromCookie() {
		try {
			String s = Cookies.getCookie(KEY_PLAYER_NAME);
			if (s == null || s.length() == 0) {
				return false;
			}
			data.playerName = Cookies.getCookie(KEY_PLAYER_NAME);
			if (data.playerName == null) {
				data.playerName = "";
			}

			s = Cookies.getCookie(KEY_HIGHSCORE);
			if (s == null || s.length() == 0) {
				s = "0";
			}
			data.highScore = Integer.parseInt(s);

			s = Cookies.getCookie(KEY_AVERAGE);
			if (s == null || s.length() == 0) {
				s = "0";
			}
			data.averageScore = Integer.parseInt(s);

			s = Cookies.getCookie(KEY_PLAY_COUNT);
			if (s == null || s.length() == 0) {
				s = "0";
			}
			data.playCount = Integer.parseInt(s);

			s = Cookies.getCookie(KEY_VICTORY_POINT);
			if (s == null || s.length() == 0) {
				s = "1300";
			}
			data.rating = Integer.parseInt(s);

			s = Cookies.getCookie(KEY_LEVEL_NAME);
			if (s == null || s.length() == 0) {
				s = "0";
			}
			data.levelName = Integer.parseInt(s);

			s = Cookies.getCookie(KEY_LEVEL_NUMBER);
			if (s == null || s.length() == 0) {
				s = "0";
			}
			data.levelNumber = Integer.parseInt(s);

			s = Cookies.getCookie(KEY_AVERAGE_RANK);
			if (s == null || s.length() == 0) {
				s = "0";
			}
			data.averageRank = Float.parseFloat(s);

			s = Cookies.getCookie(KEY_GENRE);
			if (s == null || s.length() == 0) {
				s = "0";
			}
			data.genre = Integer.parseInt(s);

			s = Cookies.getCookie(KEY_TYPE);
			if (s == null || s.length() == 0) {
				s = "0";
			}
			data.type = Integer.parseInt(s);

			data.greeting = Cookies.getCookie(KEY_GREETING);
			if (data.greeting == null) {
				data.greeting = "";
			}

			s = Cookies.getCookie(KEY_CLASS_LEVEL);
			if (s == null || s.length() == 0) {
				s = "0";
			}
			data.classLevel = Integer.parseInt(s);

			s = Cookies.getCookie(KEY_USER_CODE);
			if (s == null || s.length() == 0) {
				s = "0";
			}
			data.userCode = Integer.parseInt(s);

			data.imageFileName = Cookies.getCookie(KEY_IMAGE_FILE_NAME);
			if (data.imageFileName == null || data.imageFileName.length() == 0) {
				data.imageFileName = "noimage.jpg";
			}

			Cookies.setCookie(KEY_PLAYER_NAME, "", getRemoveTime());
			Cookies.setCookie(KEY_HIGHSCORE, "", getRemoveTime());
			Cookies.setCookie(KEY_AVERAGE, "", getRemoveTime());
			Cookies.setCookie(KEY_PLAY_COUNT, "", getRemoveTime());
			Cookies.setCookie(KEY_VICTORY_POINT, "", getRemoveTime());
			Cookies.setCookie(KEY_LEVEL_NAME, "", getRemoveTime());
			Cookies.setCookie(KEY_LEVEL_NUMBER, "", getRemoveTime());
			Cookies.setCookie(KEY_AVERAGE_RANK, "", getRemoveTime());
			Cookies.setCookie(KEY_GENRE, "", getRemoveTime());
			Cookies.setCookie(KEY_TYPE, "", getRemoveTime());
			Cookies.setCookie(KEY_GREETING, "", getRemoveTime());
			Cookies.setCookie(KEY_CLASS_LEVEL, "", getRemoveTime());
			Cookies.setCookie(KEY_IMAGE_FILE_NAME, "", getRemoveTime());

			final List<Integer> problemIds = loadList(KEY_REPORT_PROBLEMS_PREFIX);
			for (int problemId : problemIds) {
				Service.Util.getInstance().addProblemIdsToReport(data.userCode, new int[] { problemId }, Service.NULL_CALLBACK);
			}

			int keyIndex = 0;
			String value;
			while ((value = Cookies.getCookie(KEY_REPORT_PROBLEMS_PREFIX + keyIndex)) != null && value.length() != 0) {
				Cookies.setCookie(KEY_REPORT_PROBLEMS_PREFIX + keyIndex, "", getRemoveTime());
				++keyIndex;
			}
			Cookies.setCookie(KEY_REPORT_PROBLEMS_PREFIX + keyIndex++, "", getRemoveTime());
			Cookies.setCookie(KEY_REPORT_PROBLEMS_PREFIX + keyIndex, "", getRemoveTime());

			save();

			callLoadListeners();

			return true;
		} catch (Exception e) {
			e.printStackTrace();
			Controller.getInstance().showErrorMessage("UserData.loadFromCookie() : 不正な値の検知によりセーブデータが全消去されました");
		}

		// エラー発生
		return false;
	}

	private void loadFromServer() {
		Service.Util.getInstance().loadUserData(data.userCode, callbackLoadFromServer);
	}

	private final AsyncCallback<PacketUserData> callbackLoadFromServer = new AsyncCallback<PacketUserData>() {
		public void onSuccess(PacketUserData result) {
			data = result;
			Cookies.setCookie(KEY_USER_CODE, "" + data.userCode, getExpireTime());
			save();

			callLoadListeners();

			// 無視コードリスト追加(設定画面のインスタンスが作成されてから))
			SharedData.getInstance().setUserIgnoreUserCodes(data.ignoreUserCodes);

			for (int ignoreUserCode : data.ignoreUserCodes) {
				PanelSettingChat.getInstance().addIgnoreUserCodeButton(ignoreUserCode);
			}
		}

		public void onFailure(Throwable caught) {
			caught.printStackTrace();
			Controller.getInstance().showErrorMessage("UserData.callbackLoadFromServer : ユーザー情報の読み込みに失敗しました。パケットを再送します。");

			timerLoadFromServerRetry.schedule(5000);
		}
	};

	private final Timer timerLoadFromServerRetry = new SerialTimer() {
		public void run() {
			loadFromServer();
		}
	};

	private void callLoadListeners() {
		ready = true;
		for (UserDataLoadListener userDataLoadListener : loadListeners) {
			userDataLoadListener.onLoad();
		}
		loadListeners.clear();
	}

	public void addLoadListener(UserDataLoadListener listener) {
		if (ready) {
			listener.onLoad();
		} else {
			loadListeners.add(listener);
		}
	}

	public void save() {
		Cookies.setCookie(KEY_USER_CODE, "" + data.userCode, getExpireTime());
		Service.Util.getInstance().saveUserData(data, Service.NULL_CALLBACK);
	}

	private Date getExpireTime() {
		long currentTime = System.currentTimeMillis();
		currentTime += 90L * 24L * 60L * 60L * 1000L;
		return new Date(currentTime);
	}

	private Date getRemoveTime() {
		long currentTime = System.currentTimeMillis();
		currentTime -= 60L * 1000L;
		return new Date(currentTime);
	}

	public void setPlayerName(String playerName) {
		data.playerName = playerName;
	}

	public String getPlayerName() {
		return data.playerName;
	}

	public void setHighScore(int score) {
		data.highScore = score;
	}

	public int getHighScore() {
		return data.highScore;
	}

	public void setAverageScore(int score) {
		data.averageScore = score;
	}

	public int getAverageScore() {
		return data.averageScore;
	}

	public void setPlayCount(int playCount) {
		data.playCount = playCount;
	}

	public int getPlayCount() {
		return data.playCount;
	}

	public void setRating(int rating) {
		data.rating = rating;
	}

	public int getRating() {
		return data.rating;
	}

	public void setLevelName(int levelName) {
		data.levelName = levelName;
	}

	public int getLevelName() {
		return data.levelName;
	}

	public void setLevelNumber(int levelNumber) {
		data.levelNumber = levelNumber;
	}

	public int getLevelNumber() {
		return data.levelNumber;
	}

	public void setAvarageRank(float averageRank) {
		data.averageRank = averageRank;
	}

	public float getAverageRank() {
		return data.averageRank;
	}

	public void setGenre(int genre) {
		data.genre = genre;
	}

	public int getGenre() {
		return data.genre;
	}

	public void setType(int type) {
		data.type = type;
	}

	public int getType() {
		return data.type;
	}

	public void setGreeting(String greeting) {
		data.greeting = greeting;
	}

	public String getGreeting() {
		return data.greeting;
	}

	public void setClassLevel(int classLevel) {
		data.classLevel = classLevel;
	}

	public int getClassLevel() {
		return data.classLevel;
	}

	public void setUserCode(int userCode) {
		data.userCode = userCode;
		Cookies.setCookie(KEY_USER_CODE, "" + data.userCode, getExpireTime());
	}

	public int getUserCode() {
		return data.userCode;
	}

	public void setImageFileName(String imageFileName) {
		data.imageFileName = imageFileName;
	}

	public String getImageFileName() {
		return data.imageFileName;
	}

	public int[][][] getCorrectCount() {
		return data.correctCount;
	}

	public void setPlaySound(boolean playSound) {
		data.playSound = playSound;
	}

	public boolean isPlaySound() {
		return data.playSound;
	}

	public boolean isMultiGenre() {
		return data.multiGenre;
	}

	public void setMultiGenre(boolean multiGenre) {
		data.multiGenre = multiGenre;
	}

	public boolean isMultiType() {
		return data.multiType;
	}

	public void setMultiType(boolean multiType) {
		data.multiType = multiType;
	}

	public int getDifficultSelect() {
		return data.difficultSelect;
	}

	public void setDifficultSelect(int difficultSelect) {
		data.difficultSelect = difficultSelect;
	}

	public boolean isNewProblem() {
		return data.newProblem;
	}

	public void setNewProblem(boolean newProblem) {
		data.newProblem = newProblem;
	}

	public boolean isRankingMove() {
		return data.rankingMove;
	}

	public void setRankingMove(boolean rankingMove) {
		data.rankingMove = rankingMove;
	}

	public int getBbsDispInfo() {
		return data.bbsDispInfo;
	}

	public void setBbsDispInfo(int dispInfo) {
		data.bbsDispInfo = dispInfo;
	}

	public boolean isBbsAge() {
		return data.bbsAge;
	}

	public void setBbsAge(boolean age) {
		data.bbsAge = age;
	}

	public int getTimerMode() {
		return data.timerMode;
	}

	public void setTimerMode(int timerMode) {
		data.timerMode = timerMode;
	}

	public int getPrefecture() {
		return data.prefecture;
	}

	public void setPrefecture(int prefecture) {
		data.prefecture = prefecture;
	}

	public boolean isChatEnabled() {
		return data.chat;
	}

	public void setChatEnabled(boolean chatEnabled) {
		data.chat = chatEnabled;
	}

	public boolean isPublicEvent() {
		return data.publicEvent;
	}

	public void setPublicEvent(boolean publicEvent) {
		data.publicEvent = publicEvent;
	}

	public boolean isHideAnswer() {
		return data.hideAnswer;
	}

	public void setHideAnswer(boolean hideAnswer) {
		data.hideAnswer = hideAnswer;
	}

	private List<Integer> loadList(String keyPrefix) {
		int keyIndex = 0;
		final StringBuffer sb = new StringBuffer();
		String value;
		while ((value = Cookies.getCookie(keyPrefix + keyIndex++)) != null && value.length() != 0) {
			sb.append(value);
		}

		return splitInteger(sb.toString());
	}

	private List<Integer> splitInteger(String s) {
		final String split[] = s.split(DELIMITER);
		final List<Integer> list = new ArrayList<Integer>();
		for (int i = 0; i < split.length; ++i) {
			final String word = split[i];
			if (word == null || word.length() == 0) {
				continue;
			}

			try {
				list.add(Integer.parseInt(word));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return list;
	}

	@Override
	public void onClose(CloseEvent<Window> event) {
		// save();
	}
}
